#define ARDUINO_USB_MODE 1
#define ARDUINO_USB_CDC_ON_BOOT 1
// 需要esp32 3.07 版本以上(3.1.3即可，3.2.0不兼容TinyUSB)，及Adafruit TinyUSB Library库
#include "Adafruit_TinyUSB.h"
#include "MouseMotion.h"
#include "BluetoothContext.h"
#ifndef MH_ER
#define MH_ER "ER\n"
#endif
#ifndef MH_OK
#define MH_OK "OK\n"
#endif
#ifndef MH_MOUSE_REPORT_ID
#define MH_MOUSE_REPORT_ID 1
#endif
#ifndef PI
#define PI 3.141592653
#endif
uint8_t const desc_hid_report_combined[] = {
  // ===== 相对鼠标（Report ID = 1）=====
  0x05, 0x01,       // Usage Page (Generic Desktop)
  0x09, 0x02,       // Usage (Mouse)
  0xA1, 0x01,       // Collection (Application)
  0x85, 0x01,       // Report ID (1)
  0x09, 0x01,       //   Usage (Pointer)
  0xA1, 0x00,       //   Collection (Physical)
  0x05, 0x09,       //     Usage Page (Buttons)
  0x19, 0x01,       //     Usage Minimum (1)
  0x29, 0x03,       //     Usage Maximum (3)
  0x15, 0x00,       //     Logical Minimum (0)
  0x25, 0x01,       //     Logical Maximum (1)
  0x95, 0x03,       //     Report Count (3)
  0x75, 0x01,       //     Report Size (1)
  0x81, 0x02,       //     Input (Data, Variable, Absolute) - Buttons
  0x95, 0x01,
  0x75, 0x05,
  0x81, 0x03,       //     Padding
  0x05, 0x01,
  0x09, 0x30,       //     Usage (X)
  0x09, 0x31,       //     Usage (Y)
  0x09, 0x38,       //    滚轮
  0x15, 0x81,       //     Logical Minimum (-127)
  0x25, 0x7F,       //     Logical Maximum (127)
  0x75, 0x08,
  0x95, 0x03,
  0x81, 0x06,       //     Input (Data, Variable, Relative)
  0xC0,             //   End Collection
  0xC0,             // End Collection

  // ===== 绝对鼠标（Report ID = 2）=====
  // 绝对鼠标（Report ID = 2）会被识别为“触摸屏、绘图板、远程桌面控制”类设备
  // 不会像普通鼠标那样直接响应 “鼠标按下 + 拖动”
  0x05, 0x01,
  0x09, 0x02,
  0xA1, 0x01,
  0x85, 0x02,       // Report ID (2)
  0x09, 0x01,
  0xA1, 0x00,
  0x05, 0x09,
  0x19, 0x01,
  0x29, 0x03,
  0x15, 0x00,
  0x25, 0x01,
  0x95, 0x03,
  0x75, 0x01,
  0x81, 0x02,
  0x95, 0x01,
  0x75, 0x05,
  0x81, 0x03,
  0x05, 0x01,
  0x09, 0x30,
  0x09, 0x31,
  0x16, 0x00, 0x00,
  0x26, 0xFF, 0x7F,
  0x75, 0x10,
  0x95, 0x02,
  0x81, 0x02,
  0xC0,
  0xC0,

  // === 键盘 ===
  0x05, 0x01,       // Usage Page (Generic Desktop)
  0x09, 0x06,       // Usage (Keyboard)
  0xA1, 0x01,       // Collection (Application)
  0x85, 0x03,       //   Report ID (3)
  0x05, 0x07,       //   Usage Page (Key Codes)
  0x19, 0xE0,       //   Usage Minimum (224)
  0x29, 0xE7,       //   Usage Maximum (231)
  0x15, 0x00,
  0x25, 0x01,
  0x75, 0x01,
  0x95, 0x08,
  0x81, 0x02,       //   Input (Data, Variable, Absolute) - Modifiers
  0x95, 0x01,
  0x75, 0x08,
  0x81, 0x03,       //   Reserved
  0x95, 0x06,
  0x75, 0x08,
  0x15, 0x00,
  0x25, 0x65,
  0x05, 0x07,
  0x19, 0x00,
  0x29, 0x65,
  0x81, 0x00,       //   Input (Data, Array)
  0xC0              // End Collection
};

struct TaskCommand {
  int type;
  String params[20];  // 最多支持 20 个参数（改为字符串）
  int paramCount;
};

struct HIDKey {
  uint8_t keycode;
  bool needShift;
};

// 创建HID对象
Adafruit_USBD_HID usb_hid;
String inputString = "";  // 用来存储接收到的指令
int screen_width= 1920;
int screen_height= 1080;
BluetoothContext bluetoothContext;
HIDKey getHIDFunctionKeycode(const String& key) {
  HIDKey result = { HID_KEY_NONE, false };  // 默认值
  if      (key == "ESC")        result = { HID_KEY_ESCAPE, false };
  else if (key == "TAB")        result = { HID_KEY_TAB, false };
  else if (key == "CAPSLOCK")   result = { HID_KEY_CAPS_LOCK, false };
  else if (key == "SHIFT")      result = { HID_KEY_SHIFT_LEFT, false };  // 可换成 RIGHT
  else if (key == "CTRL")       result = { HID_KEY_CONTROL_LEFT, false };
  else if (key == "ALT")        result = { HID_KEY_ALT_LEFT, false };
  else if (key == "GUI")        result = { HID_KEY_GUI_LEFT, false }; // Windows / Command
  else if (key == "ENTER")      result = { HID_KEY_ENTER, false };
  else if (key == "BACKSPACE")  result = { HID_KEY_BACKSPACE, false };
  else if (key == "SPACE")      result = { HID_KEY_SPACE, false };

  // 功能键 F1~F24
  else if (key == "F1")   result = { HID_KEY_F1, false };
  else if (key == "F2")   result = { HID_KEY_F2, false };
  else if (key == "F3")   result = { HID_KEY_F3, false };
  else if (key == "F4")   result = { HID_KEY_F4, false };
  else if (key == "F5")   result = { HID_KEY_F5, false };
  else if (key == "F6")   result = { HID_KEY_F6, false };
  else if (key == "F7")   result = { HID_KEY_F7, false };
  else if (key == "F8")   result = { HID_KEY_F8, false };
  else if (key == "F9")   result = { HID_KEY_F9, false };
  else if (key == "F10")  result = { HID_KEY_F10, false };
  else if (key == "F11")  result = { HID_KEY_F11, false };
  else if (key == "F12")  result = { HID_KEY_F12, false };
  else if (key == "F13")  result = { HID_KEY_F13, false };
  else if (key == "F14")  result = { HID_KEY_F14, false };
  else if (key == "F15")  result = { HID_KEY_F15, false };
  else if (key == "F16")  result = { HID_KEY_F16, false };
  else if (key == "F17")  result = { HID_KEY_F17, false };
  else if (key == "F18")  result = { HID_KEY_F18, false };
  else if (key == "F19")  result = { HID_KEY_F19, false };
  else if (key == "F20")  result = { HID_KEY_F20, false };
  else if (key == "F21")  result = { HID_KEY_F21, false };
  else if (key == "F22")  result = { HID_KEY_F22, false };
  else if (key == "F23")  result = { HID_KEY_F23, false };
  else if (key == "F24")  result = { HID_KEY_F24, false };

  // 导航/编辑键
  else if (key == "INSERT")     result = { HID_KEY_INSERT, false };
  else if (key == "DELETE")     result = { HID_KEY_DELETE, false };
  else if (key == "HOME")       result = { HID_KEY_HOME, false };
  else if (key == "END")        result = { HID_KEY_END, false };
  else if (key == "PAGEUP")     result = { HID_KEY_PAGE_UP, false };
  else if (key == "PAGEDOWN")   result = { HID_KEY_PAGE_DOWN, false };

  // 方向键
  else if (key == "UP")         result = { HID_KEY_ARROW_UP, false };
  else if (key == "DOWN")       result = { HID_KEY_ARROW_DOWN, false };
  else if (key == "LEFT")       result = { HID_KEY_ARROW_LEFT, false };
  else if (key == "RIGHT")      result = { HID_KEY_ARROW_RIGHT, false };

  // 小键盘键
  else if (key == "NUMLOCK")    result = { HID_KEY_NUM_LOCK, false };
  else if (key == "KP_0")       result = { HID_KEY_KEYPAD_0, false };
  else if (key == "KP_1")       result = { HID_KEY_KEYPAD_1, false };
  else if (key == "KP_2")       result = { HID_KEY_KEYPAD_2, false };
  else if (key == "KP_3")       result = { HID_KEY_KEYPAD_3, false };
  else if (key == "KP_4")       result = { HID_KEY_KEYPAD_4, false };
  else if (key == "KP_5")       result = { HID_KEY_KEYPAD_5, false };
  else if (key == "KP_6")       result = { HID_KEY_KEYPAD_6, false };
  else if (key == "KP_7")       result = { HID_KEY_KEYPAD_7, false };
  else if (key == "KP_8")       result = { HID_KEY_KEYPAD_8, false };
  else if (key == "KP_9")       result = { HID_KEY_KEYPAD_9, false };
  else if (key == "KP_DOT")     result = { HID_KEY_KEYPAD_DECIMAL, false };
  else if (key == "KP_ENTER")   result = { HID_KEY_KEYPAD_ENTER, false };
  else if (key == "KP_PLUS")    result = { HID_KEY_KEYPAD_ADD, false };
  else if (key == "KP_MINUS")   result = { HID_KEY_KEYPAD_SUBTRACT, false };
  else if (key == "KP_MUL")     result = { HID_KEY_KEYPAD_MULTIPLY, false };
  else if (key == "KP_DIV")     result = { HID_KEY_KEYPAD_DIVIDE, false };

  return result;
}

HIDKey getHIDKeycode(const String& key) {
  HIDKey result = {HID_KEY_NONE, false}; // 默认值
  if (key.length() != 1) return result;
  char c = key.charAt(0);
  // 字母处理
  if (c >= 'a' && c <= 'z') {
    result.keycode = HID_KEY_A + (c - 'a');
    return result;
  }
  if (c >= 'A' && c <= 'Z') {
    result.keycode = HID_KEY_A + (c - 'A');
    result.needShift = true;
    return result;
  }
  // 数字
  if (c >= '1' && c <= '9') {
    result.keycode = HID_KEY_1 + (c - '1');
    return result;
  }
  if (c == '0') {
    result.keycode = HID_KEY_0;
    result.needShift = false;
    return result;
  }
  switch (c) {
    case '@': result = { HID_KEY_2, true }; break;  // Shift+2
    case '.': result = { HID_KEY_PERIOD, false }; break;
    case ',': result = { HID_KEY_COMMA, false }; break;
    case '!': result = { HID_KEY_1, true }; break;
    case '#': result = { HID_KEY_3, true }; break;
    case '$': result = { HID_KEY_4, true }; break;
    case '%': result = { HID_KEY_5, true }; break;
    case '^': result = { HID_KEY_6, true }; break;
    case '&': result = { HID_KEY_7, true }; break;
    case '*': result = { HID_KEY_8, true }; break;
    case '(': result = { HID_KEY_9, true }; break;
    case ')': result = { HID_KEY_0, true }; break;
    case ' ': result = { HID_KEY_SPACE, false }; break;
    default: result = { HID_KEY_NONE, false }; break;
  }
  return result;  // 不支持的字符
}

// 解析函数：输入字符串，输出解析后的 Command 结构体
bool parseTaskCommand(const String& cmdStr, TaskCommand& cmdOut) {
  if (!cmdStr.startsWith("T:")) return false;
  // 去掉前缀 T:
  String payload = cmdStr.substring(2);
  cmdOut.paramCount = 0;
  // 分割字符串
  int lastIndex = 0;
  int commaIndex = payload.indexOf(',');
  // 提取指令类型
  String typeStr = (commaIndex == -1) ? payload : payload.substring(0, commaIndex);
  cmdOut.type = typeStr.toInt();

  // 解析参数
  while (commaIndex != -1 && cmdOut.paramCount < 10) {
    lastIndex = commaIndex + 1;
    commaIndex = payload.indexOf(',', lastIndex);
    String paramStr = (commaIndex == -1)
                      ? payload.substring(lastIndex)
                      : payload.substring(lastIndex, commaIndex);
    cmdOut.params[cmdOut.paramCount++] = paramStr;
  }
  return true;
}

void onKey(String command){
  if (!command.startsWith("K:")) {
    Serial.println(MH_ER);
    return;
  }
  String keysStr = command.substring(2);  // Remove "K:"
  keysStr.trim();
  int start = 0;
  while (start < keysStr.length()) {
    int end = keysStr.indexOf(',', start);
    if (end == -1) end = keysStr.length();
    String key = keysStr.substring(start, end);
    key.trim();
    HIDKey keyCode = getHIDKeycode(key);
    if(keyCode.keycode !=HID_KEY_NONE){
      if(keyCode.needShift){
        modifyKey(KEYBOARD_MODIFIER_LEFTSHIFT,keyCode.keycode);
        delay(16);
        keyRelease();
      }else{
        enterKey(keyCode.keycode);
      }
      delay(16);
    }
    start = end + 1;
  }
  delay(50);  // Hold keys for 100ms
  Serial.println(MH_OK);
}

void onMoveMouse(String command){
  Serial0.println(command);
  // 鼠标还不行
  //if (usb_hid.ready()){
  //   Serial0.println("MH_ER 1");
  //   return;
  //}
  //不以M:开头的是错误指令
  if (!command.startsWith("M:")){
     Serial0.println(MH_ER);
     return;
  }
  //提取内容 "x,y,z"
  String params = command.substring(2);
  params.trim();
  //目标x
  int xComma = params.indexOf(',');
  //目标y
  int yComma = params.indexOf(',', xComma + 1);
  //格式不对，返回错误
  if (xComma == -1 || yComma == -1 ) {
    Serial0.println(MH_ER);
    return;
  }
  // 转换为整数类型
  // 目标x
  int x = params.substring(0, xComma).toInt();
  // 目标y
  int y = params.substring(xComma + 1, yComma).toInt();
  // 每一步的延时
  int delayMs = params.substring(yComma + 1).toInt();
  moveRelative(x,y,delayMs);
  Serial0.println(MH_OK);
}



/**
特殊指令任务
*/
void onTask(String command){
  TaskCommand cmd;
  bool parse_result = parseTaskCommand(command,cmd);
  if (!parse_result){
      Serial0.println(MH_ER);
      return;
  }
  int task_status = -1;
  switch (cmd.type) {
    case 1:
      Serial0.println("1111");
      break;
    case 2:
      // Mouse.click(MOUSE_MIDDLE);
      break;
    case 3:
      mouseClick(MOUSE_RIGHT);
      break;
    case 4:
      mouseClick(MOUSE_LEFT);
      break;
    case 5:
      screen_width = cmd.params[0].toInt();
      screen_height = cmd.params[1].toInt();
      break;
    case 6:{
      int tx = cmd.params[0].toInt();
      int ty = cmd.params[1].toInt();
      moveAbsolute(tx,ty,screen_width,screen_height);
      break;
    }
    case 7:{
      int ox = cmd.params[0].toInt();
      int oy = cmd.params[1].toInt();
      int tx = cmd.params[2].toInt();
      int ty = cmd.params[3].toInt();
      int delay = cmd.params[4].toInt();
      moveBezierAutoSteps(ox, oy, tx, ty, screen_width, screen_height, 10.0, delay);
      // 随机选择一种移动方式
      /*int moveType = random(0, 2);  // 随机选择 0 到 5 的整数（共6种方式）
      switch (moveType) {
        case 0:
            
            break;
        case 1:
            moveSineWave(ox, oy, tx, ty, screen_width, screen_height, 8);
            break;
        case 2:
            moveArc(ox, oy, tx, ty, screen_width, screen_height, 8);
            break;
        case 3:
            moveParabola(ox, oy, tx, ty, screen_width, screen_height, 8);
            break;
        case 4:
            moveRandomJitter(ox, oy, tx, ty, screen_width, screen_height, 8);
            break;
        case 5:
            moveSpiral(ox, oy, tx, ty, screen_width, screen_height, 8);
            break;
      }*/
      break;
    }
    case 8:
      modifyKey(KEYBOARD_MODIFIER_RIGHTCTRL,HID_KEY_V);
      delay(8);
      keyRelease();
      break;
    case 9:
      for(int i=0;i<cmd.paramCount;++i){
        uint8_t keyCode = cmd.params[i].charAt(0) - 'a' + HID_KEY_A;
        keyPress(keyCode);
        delay(16);
        keyRelease();
        delay(16);
      }
      enterKey(HID_KEY_1);
      delay(16);
      enterKey(HID_KEY_ENTER);
      // Serial0.printf("paramCount: (%d)\n", cmd.paramCount);
      break;
    case 10: {
      enterKey(HID_KEY_C);
      delay(16);
      enterKey(HID_KEY_V);
      delay(16);
      int count = cmd.params[0].toInt();
      enterKey(HID_KEY_1+count-1);
      delay(16);
      enterKey(HID_KEY_ENTER);
      break;
    }
    case 11:
      enterKey(HID_KEY_BACKSPACE);
      delay(16);
      break;
    case 12:{
      int scroll = cmd.params[0].toInt();
      int direction = cmd.params[1].toInt();
      mouseScroll(scroll,direction);
      break;
    }
    case 13:{
      String key = cmd.params[0];
      modifyKey(KEYBOARD_MODIFIER_LEFTALT,getHIDKeycode(key).keycode);
      delay(8);
      keyRelease();
      break;
    }
    case 14:{
      enterKey(HID_KEY_ENTER);
      delay(16);
      break;
    }
    case 15:{
      modifyKey(KEYBOARD_MODIFIER_LEFTCTRL,HID_KEY_SPACE);
      delay(8);
      keyRelease();
      break;
    }
    case 16:{
      HIDKey key = getHIDFunctionKeycode(cmd.params[0]);
      if(key.keycode!=HID_KEY_NONE){
        enterKey(key.keycode);
        delay(16);
      }
      break;
    }
    case 17:{
      String params = cmd.params[0];
      for (int i = 0; i < params.length(); i++) {
        char c = params.charAt(i);
        HIDKey key = getHIDKeycode(String(c));
        if(key.keycode!=HID_KEY_NONE){
          keyPress(key.keycode);
          delay(16);
        }
      }
      break;
    }
    case 18:{
      keyRelease();
      delay(16);
      break;
    }
    case 19:{
      String params = cmd.params[0];
      if (params=="LEFT"){
        mousePress(MOUSE_LEFT);
        delay(16);
      }else if(params=="RIGHT"){
        mousePress(MOUSE_RIGHT);
        delay(16);
      }else if(params=="MIDDLE"){
        mousePress(MOUSE_MIDDLE);
        delay(16);
      }else{
        task_status = 0;
      }
      break;
    }
    case 20:{
      int key = cmd.params[0].toInt();
      int realKey = MOUSE_NONE;
      if (key==1){
        realKey = MOUSE_LEFT;
      }else if (key==2){
        realKey = MOUSE_RIGHT;
      }else{
        realKey = MOUSE_NONE;
      }
      mouseRelease(realKey);
      delay(8);
      break;
    }
    case 21:{
      int key = cmd.params[0].toInt();
      int tx = cmd.params[1].toInt();
      int ty = cmd.params[2].toInt();
      int realKey = MOUSE_NONE;
      if (key==1){
        realKey = MOUSE_LEFT;
      }else if (key==2){
        realKey = MOUSE_RIGHT;
      }else{
        realKey = MOUSE_NONE;
      }
      pressAndMoveAbsolute(realKey, tx, ty);
      delay(16);
      break;
    }
    case 22:{
      HIDKey key = getHIDFunctionKeycode(cmd.params[0]);
      if(key.keycode!=HID_KEY_NONE){
        keyPress(key.keycode);
      }
      break;
    }
    case 23:{
      String key = cmd.params[0];
      modifyKey(KEYBOARD_MODIFIER_LEFTCTRL,getHIDKeycode(key).keycode);
      delay(8);
      keyRelease();
      break;
    }
    default:
      task_status = 0;
      break;
  }
  if(task_status==0){
      Serial0.println(MH_ER);
  }else{
      Serial0.println(MH_OK);
  }
}

void setup() {
  // 配置HID
  usb_hid.setReportDescriptor(desc_hid_report_combined, sizeof(desc_hid_report_combined));
  usb_hid.setPollInterval(2);
  usb_hid.setReportCallback(nullptr, nullptr);
  // 与setReportDescriptor似乎有些冲突，设备未识别为HID设备时需要注释掉这一句才能识别为HID
  // 一旦识别后，再去掉注释也不会再次异常，会保持是HID
  //usb_hid_mouse.setStringDescriptor("Absolute Mouse");
  usb_hid.begin();
    // 串口通信
  Serial0.begin(115200);
  delay(1000);  // 等待主机识别为 HID 鼠标
  bluetoothContext.init("任工工");
  delay(1000);
}





void loop() {
  // 检查是否有数据从串口发送过来
  if (Serial0.available() > 0) {
    // 逐个字符读取串口输入
    char incomingByte = Serial0.read();
    // 如果是换行符\n，表示一条完整指令接收完毕
    if (incomingByte == '\n') {
      //判断是否以"M:"开头，以"M:"开头是移动命令
      if (inputString.startsWith("M:")) {
        onMoveMouse(inputString);
      //判断是否以"K:"开头，以"K:"开头是按钮点击命令
      }else if(inputString.startsWith("K:")){
        onKey(inputString);
      //判断是否以"T:"开头，以"T:"开头是特殊命令
      }else if(inputString.startsWith("T:")){
        onTask(inputString);
      }else{
        Serial0.println(MH_ER);
      }
      // 清空接收缓冲区准备接收下一条指令
      inputString = "";
    } else {
      // 将接收到的字符添加到指令字符串中
      inputString += incomingByte;
    }
  }
}
